#include "pjsip/sip_auth_ocs_module.h"

#define THIS_FILE    "sip_auth_ocs_module.c"

static pj_status_t mod_auth_ocs_load(pjsip_endpoint *endpt);
static pj_status_t mod_auth_ocs_unload(void);
static pj_bool_t   mod_auth_ocs_on_rx_request(pjsip_rx_data *rdata);
static pj_bool_t   mod_auth_ocs_on_rx_response(pjsip_rx_data *rdata);
static pj_status_t   mod_auth_ocs_on_tx_request(pjsip_tx_data *tdata);
static pj_status_t   mod_auth_ocs_on_tx_response(pjsip_tx_data *tdata);




static struct auth_ocs_module
{
pjsip_module mod;

pj_pool_t		*pool;

pjsip_endpoint	*endpt; 
// table mutex
pj_mutex_t	*table_mutex;

pjsip_auth_sess_list sess_list;
}mod_auth=
{
	{
	NULL, NULL,		    /* prev, next.			*/
		{ "auth_ocs-ua", 11 },	    /* Name.				*/
		-1,			    /* Id				*/
		PJSIP_MOD_PRIORITY_TRANSPORT_LAYER+1,	/* Priority		*/
		&mod_auth_ocs_load,	    /* load()				*/
		NULL,		    /* start()				*/
		NULL,		    /* stop()				*/
		&mod_auth_ocs_unload,	    /* unload()				*/
		&mod_auth_ocs_on_rx_request,  /* on_rx_request()			*/
		&mod_auth_ocs_on_rx_response, /* on_rx_response()			*/
		&mod_auth_ocs_on_tx_request,		    /* on_tx_request.			*/
		&mod_auth_ocs_on_tx_response,		    /* on_tx_response()			*/
		NULL   /* on_tsx_state()			*/
	}
	};
/************************************************************************/
/* local function                                                       */
/************************************************************************/
/**
*
* get sess from auth_mod which.ms_opaque equals ms_opaque
*
*
* Returns:   NULL or a pointer of sess matchs ms-opaque
*
* Parameter: ms_opaque   
*/
static pjsip_auth_clt_sess* get_sess_with_msopaque(pj_str_t ms_opaque)
	{
	pjsip_auth_sess_list *node;

	node = mod_auth.sess_list.next;

	while(node!=&mod_auth.sess_list)
		{
		if (!pj_strcmp(&node->sess->ms_opaque,&ms_opaque))
			{
			return node->sess;
			}
		node = node->next;
		}
	return NULL;
	}

/**
*
* get sess from auth_mod which.endpt_key equals eipd 
* 
*
* Returns:   NULL or a pointer of sess matchs eipd
*
* Parameter: eipd   
*/
static pjsip_auth_clt_sess* get_sess_with_epid(pj_str_t eipd)
	{
	pjsip_auth_sess_list *node;
	
	node = mod_auth.sess_list.next;

	while(node!=&mod_auth.sess_list)
		{
		if (!pj_strcmp(&node->sess->endpt_key,&eipd))
			{
			pjsip_auth_mod_unlock();
			return node->sess;
			}
		node = node->next;
		}

	return NULL;
	}
static pj_status_t mod_auth_ocs_load(pjsip_endpoint *endpt)
	{
	PJ_UNUSED_ARG(endpt);
	return PJ_SUCCESS;
	}
static pj_status_t mod_auth_ocs_unload()
	{
	//some local variants

	}


/**
*
* handle incoming request msg
*
*
* Returns:   pj_bool_t
*
* Parameter: rdata   
*/
static pj_bool_t mod_auth_ocs_on_rx_request(pjsip_rx_data *rdata)
	{
		return PJ_FALSE;
	}
/**
*
* handle incoming response msg
*
*
* Returns:   pj_bool_t
*
* Parameter: rdata   
*/
static pj_bool_t mod_auth_ocs_on_rx_response(pjsip_rx_data *rdata)
	{
	    pjsip_authorization_info_hdr *hdr = NULL;
	    
	    pjsip_auth_mod_lock();
	    
	    hdr = (pjsip_authorization_info_hdr*)pjsip_msg_find_hdr(rdata->msg_info.msg,PJSIP_H_AUTHENTICATION_INFO,NULL);
	    if (hdr!=NULL)
	    {
	        pjsip_cached_auth *cached_auth = NULL;
			pjsip_auth_clt_sess *sess = get_sess_with_epid(rdata->msg_info.from->epid);
			cached_auth = sess->cached_auth.next;
			while(cached_auth!=&sess->cached_auth)
			{
				if (!pj_strcmp(&cached_auth->scheme,&hdr->scheme)&&!pj_strcmp(&cached_auth->realm,&hdr->credential.common.realm))
				{
					if (cached_auth->auth_status == AUTH_STATUS_ESTABLISHING)
					{
						cached_auth->auth_status = AUTH_STATUS_ESTABLISHED;
					}
				}
			}
			
	    }
	    
	    pjsip_auth_mod_unlock();
	    
		return PJ_FALSE;
	}


/**
*
* handle outgoing request 
*
*
* Returns:   pj_bool_t
*
* Parameter: tdata   
*/
static pj_status_t mod_auth_ocs_on_tx_request(pjsip_tx_data *tdata)
	{
		pjsip_from_hdr * hdr_from = NULL;
		pjsip_auth_clt_sess *sess = NULL;
		pj_status_t status;
		
		hdr_from = (pjsip_from_hdr*)pjsip_msg_find_hdr(tdata->msg,PJSIP_H_FROM,NULL);
		
		pjsip_auth_mod_lock();
		
		sess = get_sess_with_epid(hdr_from->epid);
		
		status = pjsip_auth_make_signature(sess,tdata);
		
		pjsip_auth_mod_unlock();
		return status;
	}

/**
*
* handle outgoing response
*
*
* Returns:   pj_bool_t
*
* Parameter: tdata   
*/
static pj_status_t mod_auth_ocs_on_tx_response(pjsip_tx_data *tdata)
	{
		return PJ_SUCCESS;
	}


/************************************************************************/
/* so called public function                                            */
/************************************************************************/
PJ_DEF(pj_status_t) pjsip_auth_layer_init_module(pjsip_endpoint *endpt)
	{
	pj_status_t ss;


	PJ_ASSERT_RETURN(mod_auth.endpt==NULL,PJ_EINVALIDOP);

	mod_auth.endpt=endpt;
	mod_auth.pool = pjsip_endpt_create_pool(endpt,"auth%p",512,512);
	if (mod_auth.pool==NULL)
		{
		return PJ_ENOMEM;
		}
	ss= pj_mutex_create_recursive(mod_auth.pool,"auth%p",&mod_auth.table_mutex);
	if (ss!=PJ_SUCCESS)
		{
		goto on_error;
		}

	pj_list_init(&mod_auth.sess_list);


	ss =pjsip_endpt_register_module(endpt,&mod_auth.mod);
	if (ss!=PJ_SUCCESS)
	 {
	 pj_mutex_destroy(mod_auth.table_mutex);
	 goto on_error;
	 }
	return PJ_SUCCESS;
on_error:
	if (mod_auth.pool)
		{
		pjsip_endpt_release_pool(endpt,mod_auth.pool);
		mod_auth.pool = NULL;
		}
	return ss;
	}


PJ_DEF(pj_status_t) pjsip_auth_layer_destroy(void)
	{
	pj_status_t ss;
	unsigned i,j;
	pj_hash_iterator_t *it,itbuf;
	// make sure we have init auth_layer
	PJ_ASSERT_RETURN(mod_auth.endpt!=NULL,PJ_EINVALIDOP);

	//unregister module ,
	pjsip_endpt_unregister_module(mod_auth.endpt,&mod_auth.mod);


	ss= pj_mutex_destroy(mod_auth.table_mutex);

	pj_pool_release(mod_auth.pool);

	return PJ_SUCCESS;
	}


PJ_DEF(pj_status_t) pjsip_auth_layer_add_auth_sess(pjsip_auth_clt_sess *sess)
	{
		pjsip_auth_sess_list *sess_list_node;

		PJ_ASSERT_RETURN(mod_auth.endpt!=NULL,PJ_EINVALIDOP);

		sess_list_node = (pjsip_auth_sess_list*)PJ_POOL_ZALLOC_T(mod_auth.pool,pjsip_auth_sess_list);
		sess_list_node->sess = sess;
		
		pjsip_auth_mod_lock();
		
		pj_list_insert_before(&mod_auth.sess_list,sess_list_node);
		
		pjsip_auth_mod_unlock();
	}

PJ_DEF(void) pjsip_auth_mod_lock()
	{
		pj_mutex_lock(mod_auth.table_mutex);
	}
PJ_DEF(void) pjsip_auth_mod_unlock()
	{
		pj_mutex_unlock(mod_auth.table_mutex);
	}

